/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

/**
 * I/O流处理工具类
 *
 * @auther ebert_chan
 */
public class StreamUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(StreamUtils.class);

    private static final String SEPARATOR = "/";

    private StreamUtils() {
    }

    public static String url(String... path) {
        StringBuilder sb = new StringBuilder();
        for (String sub : path) {
            url(sb, sub);
        }
        return sb.toString();
    }

    /**
     * 拼接url
     *
     * @param domain
     * @param uri
     * @return
     * @auther ebert_chan
     */
    public static String url(String domain, String uri) {
        if (StringUtils.isEmpty(uri) || StringUtils.isEmpty(domain) || uri.startsWith("http")) {
            return uri;
        }
        StringBuilder sb = new StringBuilder(domain);
        url(sb, uri);
        return sb.toString();
    }

    private static StringBuilder url(StringBuilder buffer, String path) {
        if (StringUtils.isEmpty(path)) {
            return buffer;
        }

        int index = buffer.lastIndexOf(SEPARATOR);
        boolean isSeparatorEnd = index == buffer.length() - 1;

        if (!path.startsWith(SEPARATOR) && !isSeparatorEnd) {
            buffer.append(SEPARATOR);
        }
        if (isSeparatorEnd && path.startsWith(SEPARATOR)) {
            buffer.deleteCharAt(index);
        }
        buffer.append(path);
        return buffer;
    }

    /**
     * 通过name获取类路径下的文件输入流
     *
     * @param name
     * @return
     * @auther ebert_chan
     */
    public static InputStream stream(String name) {
        return StreamUtils.class.getResourceAsStream(name);
    }

    /**
     * 从文件创建输入流
     *
     * @param filePath
     * @return
     * @auther ebert_chan
     */
    public static InputStream file2Stream(String filePath) {
        InputStream fis = null;
        try {
            File file = new File(filePath);
            fis = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            LOGGER.error("file not found", e);
        }
        return fis;
    }

    /**
     * 输入流转换为byte[]
     *
     * @param fis
     * @return
     * @auther ebert_chan
     */
    public static byte[] stream2Byte(InputStream fis) {
        byte[] buffer = null;
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            LOGGER.error("file not found", e);
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(bos);
            closeStream(fis);
        }
        return buffer;
    }

    /**
     * 文件转换为byte[]
     *
     * @param filePath
     * @return
     * @auther ebert_chan
     */
    public static byte[] file2Byte(String filePath) {
        return file2Byte(new File(filePath));
    }

    /**
     * 文件转换为byte[]
     *
     * @param file
     * @return
     * @auther ebert_chan
     */
    public static byte[] file2Byte(File file) {
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        byte[] buffer = null;
        try {
            fis = new FileInputStream(file);
            bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            LOGGER.error("file not found", e);
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(bos);
            closeStream(fis);
        }
        return buffer;
    }

    /**
     * 对象转换为byte[]
     *
     * @param o
     * @return
     * @auther ebert_chan
     */
    public static <T extends Serializable> byte[] object2Byte(T o) {
        byte[] buffer = null;

        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;

        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);

            oos.writeObject(o);
            oos.flush();

            buffer = bos.toByteArray();
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(oos);
            closeStream(bos);
        }
        return buffer;
    }

    /**
     * 二进制数据转对象
     *
     * @param data
     * @return
     * @auther ebert_chan
     */
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T byte2Object(byte[] data) {
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        T o = null;
        try {
            bis = new ByteArrayInputStream(data);
            ois = new ObjectInputStream(bis);

            o = (T) ois.readObject();

        } catch (ClassNotFoundException e) {
            LOGGER.error("class not found.", e);
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(ois);
            closeStream(bis);
        }
        return o;
    }

    /**
     * 二进制数据写入到文件
     *
     * @param data
     * @param filePath
     * @return
     * @auther ebert_chan
     */
    public static boolean byte2File(byte[] data, String filePath) {
        FileOutputStream out = null;
        try {
            filePath = filePath.replaceAll("\\\\", SEPARATOR);
            File file = new File(filePath.substring(0, filePath.lastIndexOf(SEPARATOR) + 1));
            if (!file.exists()) {
                file.mkdirs();
            }
            out = new FileOutputStream(filePath);
            out.write(data, 0, data.length);
            out.flush();
            return true;
        } catch (FileNotFoundException e) {
            LOGGER.error("file not found", e);
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(out);
        }
        return false;
    }

    /**
     * 二进制数据写入到文件
     *
     * @param data
     * @param filePath
     * @return
     * @auther ebert_chan
     */
    public static boolean byte2File(byte[] data, File file) {
        FileOutputStream out = null;
        try {
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }
            out = new FileOutputStream(file);
            out.write(data, 0, data.length);
            out.flush();
            return true;
        } catch (FileNotFoundException e) {
            LOGGER.error("file not found", e);
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(out);
        }
        return false;
    }

    /**
     * 复制文件
     *
     * @param source
     * @param dest
     * @auther ebert_chan
     */
    public static void copy(String source, String dest) {
        FileInputStream in = null;
        FileOutputStream output = null;
        try {
            File s = new File(source);
            in = new FileInputStream(s);

            File destFile = new File(dest);
            File parent = destFile.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }

            output = new FileOutputStream(destFile);
            byte[] b = new byte[1024];
            int n;
            while ((n = in.read(b)) != -1) {
                output.write(b, 0, n);
            }
            output.flush();
        } catch (FileNotFoundException e) {
            LOGGER.error("file not found", e);
        } catch (IOException e) {
            LOGGER.error("io exception", e);
        } finally {
            closeStream(in);
            closeStream(output);
        }
    }

    /**
     * 移动文件
     *
     * @param source
     * @param dest
     * @auther ebert_chan
     */
    public static boolean move(String source, String dest) {
        File s = new File(source);
        if (s.exists()) {
            copy(source, dest);
            if (s.delete()) {
                // donothing
            }
            return true;
        }
        return false;
    }

    /**
     * 关闭流
     *
     * @param in
     * @auther ebert_chan
     */
    public static void closeStream(Closeable in) {
        try {
            if (in != null) {
                in.close();
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * 关闭流
     *
     * @param in
     * @auther ebert_chan
     */
    public static void closeStream(InputStream in) {
        try {
            if (in != null) {
                in.close();
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * 关闭流
     *
     * @param in
     * @auther ebert_chan
     */
    public static void closeStream(OutputStream out) {
        try {
            if (out != null) {
                out.close();
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    }

}
